﻿using System;
using System.Linq;

namespace Inovout
{
    public class DateTimeRange : IComparable<DateTimeRange>, IComparable
    {
        public DateTimeRange(DateTime start, DateTimeRangeKind kind)
        {
            this.Start = start;
            this.Kind = kind;
            switch (kind)
            {
                case DateTimeRangeKind.Year:
                    this.Start = new DateTime(start.Year, 1, 1);
                    this.End = this.Start.AddYears(1).AddMilliseconds(-1);
                    break;
                case DateTimeRangeKind.Quarter:
                    int month = start.Month;
                    if (new int[] { 2, 5, 8, 11 }.Contains(month))
                    {
                        month--;
                    }
                    else if (new int[] { 3, 6, 9, 12 }.Contains(month))
                    {
                        month -= 2;
                    }
                    this.Start = new DateTime(start.Year, month, 1);
                    this.End = this.Start.AddMonths(3).AddMilliseconds(-1);
                    break;
                case DateTimeRangeKind.DoubleMonth:
                    this.Start = new DateTime(start.Year, start.Month % 2 == 0 ? start.Month - 1 : start.Month, 1);
                    this.End = this.Start.AddMonths(2).AddMilliseconds(-1);
                    break;
                case DateTimeRangeKind.Month:
                    this.Start = new DateTime(start.Year, start.Month, 1);
                    this.End = this.Start.AddMonths(1).AddMilliseconds(-1);
                    break;
                case DateTimeRangeKind.Week:
                    this.Start = new DateTime(start.Year, start.Month, start.Day).AddDays(-1 * (int)start.DayOfWeek);

                    this.End = this.Start.AddDays(7).AddMilliseconds(-1);
                    break;
                case DateTimeRangeKind.Day:
                    this.Start = new DateTime(start.Year, start.Month, start.Day);
                    this.End = this.Start.AddDays(1).AddMilliseconds(-1);
                    break;
                case DateTimeRangeKind.Hour:
                    this.Start = new DateTime(start.Year, start.Month, start.Day, start.Hour, 0, 0);
                    this.End = this.Start.AddHours(1).AddMilliseconds(-1);
                    break;
                case DateTimeRangeKind.Minute:
                    this.Start = new DateTime(start.Year, start.Month, start.Day, start.Hour, start.Minute, 0);
                    this.End = this.Start.AddMinutes(1).AddMilliseconds(-1);
                    break;
                case DateTimeRangeKind.Second:
                    this.Start = new DateTime(start.Year, start.Month, start.Day, start.Hour, start.Minute, start.Second);
                    this.End = this.Start.AddSeconds(1).AddMilliseconds(-1);
                    break;
                case DateTimeRangeKind.Millisecond:
                    this.Start = this.End = start;
                    break;
            }
        }
        public DateTime Start { get; set; }
        public DateTime End { get; set; }
        public DateTimeRangeKind Kind { get; set; }

        public override bool Equals(object obj)
        {
            DateTimeRange that = obj as DateTimeRange;
            if (this == null)
            {
                return false;
            }
            return this.Start.Equals(that.Start) && this.End.Equals(that.End) && this.Kind.Equals(that.Kind);
        }
        public override int GetHashCode()
        {
            return this.Start.GetHashCode() ^ this.End.GetHashCode() ^ this.Kind.GetHashCode();
        }
        public override string ToString()
        {
            switch (this.Kind)
            {
                case DateTimeRangeKind.Year:
                    return this.Start.ToString("yyyy");
                 case DateTimeRangeKind.Month:
                case DateTimeRangeKind.DoubleMonth:
                case DateTimeRangeKind.Quarter:
                    return this.Start.ToString("yyyy-M");
                case DateTimeRangeKind.Week:
                case DateTimeRangeKind.Day:
                    return this.Start.ToString("yyyy-M-d");
                case DateTimeRangeKind.Hour:
                    return this.Start.ToString("yyyy-M-d h");
                case DateTimeRangeKind.Minute:
                    return this.Start.ToString("yyyy-M-d h:m");
                case DateTimeRangeKind.Second:
                    return this.Start.ToString("yyyy-M-d h:m:s");
                case DateTimeRangeKind.Millisecond:
                    return this.Start.ToString();
            }
            return base.ToString();
        }

        public int CompareTo(DateTimeRange other)
        {
            return this.Start.CompareTo(other.Start);
        }

        public bool In(DateTime value)
        {
            return value >= Start && value <= End;
        }
        public bool Out(DateTime value)
        {
            return !In(value);
        }

        public int CompareTo(object obj)
        {
            return this.CompareTo(obj as DateTimeRange);
        }
    }
}
